home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1995 November
/
EnigmA AMIGA RUN 02 (1995)(G.R. Edizioni)(IT)[!][issue 1995-11][Skylink CD].iso
/
earcd
/
util
/
text
/
words3.lha
/
Words
/
SpellIT
/
source
/
main.c
next >
Wrap
C/C++ Source or Header
|
1995-08-31
|
45KB
|
1,582 lines
/* -----------------------------------------------------------------------------
SpellIT API client, ©1995 Dietmar Eilert. Dice:
dcc main.c -// -proto -mi -r -2.0 -l localess.lib -l reqtoolss.lib -o ram:SpellIT
Note: Compiling this code requires reqtools includes & reqtools linker
libraries. The ReqTools library is © Nico François.
The following code adds spell checking capabilities to GoldED. It is based on
the ISpell International package. You need new the ISpell 3.1 from AmiNet, not
the old ISpell 3.3LJR distribution. ISpell has to be installed properly before
you can use this API client. This example uses synchronous ARexx communication:
Requests are PutMsg()'ed to GoldED's port, followed by a WaitPort() to get the
editor's response. This works fine since we need no ARexx communication after
the API link has been established. If there were ARexx communication with
GoldED AFTER the link has been established (i.e. after sending the 'API
PORT=...' command to register with GoldED), we would have to use an
asynchronous design beeing capable of answering incoming API messages while
waiting for completion of ARexx requests sent to GoldED.
-------------------------------------------------------------------------------
*/
/// "includes"
#define Prototype extern
#include <exec/exec.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dos/dos.h>
#include <dos/dostags.h>
#include <dos/rdargs.h>
#include <intuition/intuition.h>
#include <utility/tagitem.h>
#include <libraries/gadtools.h>
#include <libraries/reqtools.h>
#include <libraries/locale.h>
#include <workbench/startup.h>
#include <rexx/errors.h>
#include <rexx/rxslib.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/diskfont_protos.h>
#include <clib/rexxsyslib_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/reqtools_protos.h>
#include <clib/locale_protos.h>
#include <clib/alib_protos.h>
#include <devices/audio.h>
#include "golded:api/include/golded.h"
#include "strings.c"
#define EDIT_MAXWIDTH 999 // maximum line width of editor
#define USERVAR_ONLINE 18 // user var ID
#define USERVAR_ASK 19 // user var ID
// private API definitions
struct SpellConfig {
struct MsgPort *RexxPort; // our message port
UWORD Volume; // beep volume
BOOL Ask; // show suggestions ?
BOOL OnlineCheck; // check while typing ?
char **AppStrings; // localized strings
UWORD Count; // new word counter
BOOL TeX; // ignore word starting with '\' ?
BOOL Resident; // keep ISpell resident
UWORD Column; // cursor column
ULONG Line; // cursor line
UBYTE *Buffer; // current line: data
UWORD Len; // current line: size
};
Prototype BOOL Exists(UBYTE *);
Prototype void main(int, char **);
Prototype int wbmain(struct WBStartup *);
Prototype BOOL InitISpell(UBYTE *, UBYTE *);
Prototype void HandleAPI(char *, struct SpellConfig *);
Prototype ULONG *SendRexxCommand(char *, char *, struct MsgPort *, char *);
Prototype void CheckOnlineWord(struct APIMessage *, struct SpellConfig *);
Prototype void Beep(UWORD);
Prototype UWORD ComputeX(struct TextFont *, UWORD);
Prototype UWORD ComputeY(struct TextFont *, UWORD);
Prototype char *ShowSpell(char *, struct List *, char *);
Prototype struct Node *SearchNode(struct List *, UWORD);
Prototype void Dispatch(struct APIMessage *, struct SpellConfig *);
Prototype LONG CommandSpell(ULONG *, struct APIMessage *, struct SpellConfig *);
Prototype BOOL EvaluateBool(BOOL *, UWORD, ULONG *);
Prototype UWORD Information(struct APIMessage *, char *, char *);
Prototype BOOL AlertInfo(char *);
Prototype LONG FindNextError(struct APIMessage *, struct SpellConfig *);
Prototype char *CheckThisWord(struct APIMessage *, struct SpellConfig *, char *, UWORD, BOOL, UWORD);
Prototype void CheckCurrentWord(struct APIMessage *, struct SpellConfig *, BOOL, UWORD);
Prototype LONG AddCurrentWord(struct APIMessage *, struct SpellConfig *);
Prototype void FlushUserDictionary(struct APIMessage *, struct SpellConfig *);
Prototype void RememberCursorPosition(struct APIMessage *, struct SpellConfig *);
Prototype BOOL DoNextWord(struct APIMessage *, struct SpellConfig *, BOOL);
Prototype BOOL IsASpace(UBYTE, BOOL);
Prototype BOOL DoRightSmart(struct APIMessage *, struct SpellConfig *);
Prototype void DoRight(struct APIMessage *, struct SpellConfig *);
Prototype void DoDown (struct APIMessage *, struct SpellConfig *);
Prototype void Replace(struct APIMessage *, struct SpellConfig *, UWORD, UWORD, char *);
struct Library *ReqToolsBase;
struct Library *LocaleBase;
///
/// "init"
/* -------------------------------- InitISpell ---------------------------------
Prepare ISpell. Return TRUE if successful.
*/
BOOL
InitISpell(language, args)
UBYTE *language, *args;
{
if (FindPort("IRexxSpell"))
return(TRUE);
else {
static UBYTE command[255], hashfile[255], personal[255], home[255];
BPTR handle;
// $HOME required by ISpell 3.1.18
if (GetVar("HOME", home, sizeof(home), GVF_GLOBAL_ONLY) == -1)
SetVar("HOME", "sys:", -1, GVF_GLOBAL_ONLY);
if (language == NULL)
language = "english";
sprintf(hashfile, "ispell:lib/%s.hash", language, language);
sprintf(personal, "/ispell/lib/.ispell_%s", language);
if (Exists(hashfile)) {
if (handle = Open("NIL:", MODE_NEWFILE)) {
struct TagItem tags[] = {
SYS_Input, handle,
SYS_Output, NULL,
SYS_Asynch, TRUE,
NP_StackSize, 25600,
TAG_DONE
};
// run ISpell in host mode
if (args)
sprintf(command, "ispell -d%s -p%s -r %s", hashfile, personal, args);
else
sprintf(command, "ispell -d%s -p%s -r", hashfile, personal);
if (SystemTagList(command, tags) != -1) {
UWORD try;
for (try = 100; try--; Delay(10)) {
Forbid();
if (FindPort("IRexxSpell"))
break;
Permit();
}
if (try)
return(TRUE);
}
else
Close(handle);
AlertInfo("ISpell not available");
}
}
else {
strins(hashfile, "dictionary missing: ");
AlertInfo(hashfile);
}
}
return(FALSE);
}
///
/// "main"
int
wbmain(struct WBStartup *wbs)
{
main(0, NULL);
}
void
main(int argc, char **argv)
{
static UBYTE version[] = "$VER: SpellIT 2.0 (" __COMMODORE_DATE__ ")";
static UBYTE *appStrings[] = {
NOMOREERRORS_STR,
ATTEMPTTOREGISTERFAILED_STR,
TOBEUSEDASCLIENT_STR,
WORDSADDEDSAVECHANGES_STR,
YESNO_STR,
ABOUT_STR,
NOWORDUNDERCURSOR_STR,
REQTOOLSLIBRARYMISSING_STR,
NULL
};
struct Catalog *catalog;
if (LocaleBase = OpenLibrary("locale.library", 0)) {
struct TagItem tags[] = { OC_BuiltInLanguage, "english", TAG_DONE };
ULONG id;
if (catalog = OpenCatalogA(NULL, "spellIT.catalog", tags))
for (id = 0; appStrings[id]; id++)
appStrings[id] = GetCatalogStr(catalog, id, appStrings[id]);
}
if (ReqToolsBase = OpenLibrary("reqtools.library", 0)) {
ULONG argArray[] = { 0, 0, 0, 0 };
struct RDArgs *rdArgs;
if (rdArgs = ReadArgs("RESIDENT/S,H=HOST/K/A,LANGUAGE/K,ARGS/K", argArray, NULL)) {
struct SpellConfig spellConfig = {
NULL, // no reply port set up so far
32 // beep volume
FALSE, // don't show suggestions
FALSE, // no online check
appStrings, // localized strings
0, // counter: no words added so far
FALSE, // don't ignore \TeX commands
argArray[0] // keep ISpell resident
};
if (InitISpell((UBYTE *)argArray[2], (UBYTE *)argArray[3]))
HandleAPI((UBYTE *)argArray[1], &spellConfig);
FreeArgs(rdArgs);
}
else
Information(NULL, appStrings[TOBEUSEDASCLIENT], NULL);
CloseLibrary(ReqToolsBase);
}
else
AlertInfo(appStrings[REQTOOLSLIBRARYMISSING]);
if (LocaleBase) {
CloseCatalog(catalog);
CloseLibrary(LocaleBase);
}
exit(0);
}
///
/// "api management"
/* --------------------------------- HandleAPI ---------------------------------
Register with GoldED & handle incoming API messages.
*/
void
HandleAPI(host, spellConfig)
char *host;
struct SpellConfig *spellConfig;
{
struct MsgPort *apiPort;
if (apiPort = CreateMsgPort()) {
if (spellConfig->RexxPort = CreateMsgPort()) {
char command[255];
ULONG *result;
sprintf(command, "API PORT=%ld CLASS=%ld", apiPort, API_CLASS_ROOT | API_CLASS_KEY | API_CLASS_REXX);
if (result = SendRexxCommand(host, command, spellConfig->RexxPort, NULL)) {
if (*result == RC_OK) {
BOOL active = TRUE;
do {
struct APIMessage *apiMsg, *nextMsg;
while (!(apiMsg = (struct APIMessage *)GetMsg(apiPort)))
WaitPort(apiPort);
do {
for (nextMsg = apiMsg; nextMsg; nextMsg = nextMsg->api_Next) {
if (nextMsg->api_State == API_STATE_NOTIFY) {
spellConfig->OnlineCheck = (nextMsg->api_Global->F_User)[USERVAR_ONLINE];
spellConfig->Ask = (nextMsg->api_Global->F_User)[USERVAR_ASK];
switch (nextMsg->api_Class) {
case API_CLASS_REXX:
switch (nextMsg->api_Action) {
case API_ACTION_COMMAND:
Dispatch(nextMsg, spellConfig);
break;
default:
nextMsg->api_Error = API_ERROR_UNKNOWN;
}
break;
case API_CLASS_ROOT:
switch (nextMsg->api_Action) {
case API_ACTION_DIE:
FlushUserDictionary(nextMsg, spellConfig);
active = FALSE;
break;
case API_ACTION_INTRODUCE:
static struct TagItem tags[] = {
API_Client_Name, "SpellIT",
API_Client_Copyright, "SpellIT ©1995 Dietmar Eilert",
API_Client_Purpose,
"SpellIT is a spell checker, adding online spell \n"
"checking capabilities to GED. Default bindings \n"
"if using the WORDS keymap: \n"
" \n"
" F6 ............ online spell checking ON \n"
"SHIFT-F6 ............ online spell checking OFF \n"
" F7 ............ move cursor to next error \n"
" F8 ............ check word under cursor \n"
" F9 ............ learn word under cursor \n"
" F10 ........... normal error beep \n"
"SHIFT-F10 ........... low beep \n",
API_Client_Template, "SPELL ONLINE/K,ABOUT/S,ASK/K,NEXT/S,SUGGEST/S,VOLUME/N,ADD/S,CURRENT/S,TEX/K",
TAG_DONE
};
nextMsg->api_Data = tags;
break;
default:
nextMsg->api_Error = API_ERROR_UNKNOWN;
}
break;
case API_CLASS_KEY:
switch (nextMsg->api_Action) {
case API_ACTION_VANILLAKEY:
if (spellConfig->OnlineCheck) {
// checks are performed after white space characters:
if (IsASpace((UBYTE)nextMsg->api_Data, FALSE))
CheckOnlineWord(nextMsg, spellConfig);
}
break;
default:
nextMsg->api_Error = API_ERROR_UNKNOWN;
}
break;
default:
nextMsg->api_Error = API_ERROR_UNKNOWN;
}
}
}
ReplyMsg((struct Message *)apiMsg);
} while (apiMsg = (struct APIMessage *)GetMsg(apiPort));
} while (active);
}
}
else
AlertInfo(spellConfig->AppStrings[ATTEMPTTOREGISTERFAILED]);
if (!spellConfig->Resident)
SendRexxCommand("IRexxSpell", "EXIT", spellConfig->RexxPort, NULL);
DeleteMsgPort(spellConfig->RexxPort);
}
DeleteMsgPort(apiPort);
}
}
///
/// "arexx class"
/* --------------------------------- Dispatch ----------------------------------
Dispatch incoming command: examine command string (command part is uppercase
already), look for handler function related to command, parse arguments (if
command supports arguments), call handler.
*/
void
Dispatch(apiMsg, spellConfig)
struct APIMessage *apiMsg;
struct SpellConfig *spellConfig;
{
struct RDArgs *rdArgs, *args;
if (rdArgs = AllocDosObject(DOS_RDARGS, NULL)) {
static char buffer[1024];
// table of supported commands, associated handlers & template strings
static struct parser { char *command; LONG (*handler)(ULONG *, struct APIMessage *, struct SpellConfig *); char *template; } parser[] = {
"SPELL", (APTR)CommandSpell, "ONLINE/K,ABOUT/S,ASK/K,NEXT/S,SUGGEST/S,VOLUME/N,ADD/S,CURRENT/S,TEX/K",
NULL
};
ULONG n, argArray[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
struct APIRexxNotify *notify = (struct APIRexxNotify *)apiMsg->api_Data;
// make LF-terminated copy of command string (required by dos/readArgs):
strcpy(buffer, notify->arn_Command);
strcat(buffer, "\12");
for (n = 0; parser[n].command; ++n) {
if (!memcmp(buffer, parser[n].command, strlen(parser[n].command))) {
char *arguments = buffer + strlen(parser[n].command);
rdArgs->RDA_Source.CS_Buffer = arguments;
rdArgs->RDA_Source.CS_Length = strlen(arguments);
rdArgs->RDA_Source.CS_CurChr = 0;
rdArgs->RDA_DAList = NULL;
rdArgs->RDA_Buffer = NULL;
if (parser[n].template) {
if (args = ReadArgs(parser[n].template, argArray, rdArgs)) {
notify->arn_RC = (*parser[n].handler)(argArray, apiMsg, spellConfig);
FreeArgs(args);
}
else {
static char errorText[81];
notify->arn_RC = RC_WARN;
notify->arn_CommandError = errorText;
Fault(IoErr(), "IoErr()", errorText, 80);
}
}
else
notify->arn_RC = (*parser[n].handler)(argArray, apiMsg, spellConfig);
}
}
FreeDosObject(DOS_RDARGS, rdArgs);
}
}
/* -------------------------------- CommandSpell -------------------------------
ISpell interface
template: ONLINE/K,ABOUT/S,ASK/K,NEXT/S,SUGGEST/S,VOLUME/N,ADD/S,CURRENT/S,TEX/K
*/
LONG
CommandSpell(argArray, apiMsg, spellConfig)
ULONG *argArray;
struct APIMessage *apiMsg;
struct SpellConfig *spellConfig;
{
apiMsg->api_State = API_STATE_CONSUMED;
LONG error = RC_OK;
RememberCursorPosition(apiMsg, spellConfig);
if (argArray[0]) // ONLINE/K
apiMsg->api_Global->F_User[USERVAR_ONLINE] = EvaluateBool(&spellConfig->OnlineCheck, 0, argArray);
if (argArray[2]) // ASK/K
apiMsg->api_Global->F_User[USERVAR_ASK] = EvaluateBool(&spellConfig->Ask, 2, argArray);
EvaluateBool(&spellConfig->TeX, 8, argArray); // TEX/K
if (argArray[5]) // VOLUME/N
spellConfig->Volume = *(ULONG *)argArray[5];
if (argArray[1]) // ABOUT/S
Information(apiMsg, spellConfig->AppStrings[ABOUT], NULL);
if (argArray[6]) // ADD/S
error |= AddCurrentWord(apiMsg, spellConfig);
if (argArray[4]) // SUGGEST/S
CheckCurrentWord(apiMsg, spellConfig, TRUE, FALSE);
if (argArray[7]) // CURRENT/S
CheckCurrentWord(apiMsg, spellConfig, spellConfig->Ask, spellConfig->Volume);
if (argArray[3]) // NEXT/S
error |= FindNextError(apiMsg, spellConfig);
return(error);
}
///
/// "key class"
/* -------------------------- RememberCursorPosition ---------------------------
Remember current cursor position
*/
void
RememberCursorPosition(apiMsg, spellConfig)
struct APIMessage *apiMsg;
struct SpellConfig *spellConfig;
{
struct EditConfig *config = apiMsg->api_Config;
spellConfig->Column = config->Column;
spellConfig->Line = config->Line;
spellConfig->Buffer = config->CurrentBuffer;
spellConfig->Len = config->CurrentLen;
}
/* ---------------------------- CheckOnlineWord ---------------------------------
Check word. This function is called BEFORE GoldED actually inserts a white
space character (space, colon, ...) at the current cursor position. We assume
the current position to be a white space character to guarantee correct
behavior if the user inserts a character into a word. If ISpell complains about
a word we will either issue a beep (ask = FALSE) or additionally display a list
of suggestions (<ask> = TRUE).
Excerpt taken from man/ispell.1: If the word is not in the dictionary, but
there are near misses, then the result line contains an '&', a space, and a
list of the near misses separated by spaces.
*/
void
CheckOnlineWord(apiMsg, spellConfig)
struct APIMessage *apiMsg;
struct SpellConfig *spellConfig;
{
struct EditConfig *config = apiMsg->api_Config;
UWORD column;
UBYTE *next;
RememberCursorPosition(apiMsg, spellConfig);
column = spellConfig->Column;
next = spellConfig->Buffer + column;
// cursor placed directly after word ?
if (column && !IsASpace(*(next - 1), FALSE)) {
UBYTE *replacement;
UWORD wordLen;
// find beginning of word
for (wordLen = 0; column && !IsASpace(*(next - 1), FALSE); --column, --next)
++wordLen;
if (replacement = CheckThisWord(apiMsg, spellConfig, next, wordLen, spellConfig->Ask, spellConfig->Volume))
if (replacement != (UBYTE *)TRUE)
Replace(apiMsg, spellConfig, column, wordLen, replacement);
}
}
///
/// "ISpell"
/* ---------------------------- FlushUserDictionary ----------------------------
Aks wether to flush user dictionary to disk
*/
void
FlushUserDictionary(apiMsg, spellConfig)
struct APIMessage *apiMsg;
struct SpellConfig *spellConfig;
{
if (spellConfig->Count) {
char buffer[80];
sprintf(buffer, spellConfig->AppStrings[WORDSADDEDSAVECHANGES], spellConfig->Count);
if (Information(apiMsg, buffer, spellConfig->AppStrings[YESNO]))
SendRexxCommand("IRexxSpell", "ADD A", spellConfig->RexxPort, NULL);
}
}
/* ----------------------------- CheckCurrentWord ------------------------------
Check word. If ISpell complains about a word we will either issue a beep (ask =
FALSE) or additionally display a list of suggestions (<ask> = TRUE).
*/
void
CheckCurrentWord(apiMsg, spellConfig, ask, volume)
struct APIMessage *apiMsg;
struct SpellConfig *spellConfig;
BOOL ask;
UWORD volume;
{
struct EditConfig *config = apiMsg->api_Config;
UWORD column, wordLen;
UBYTE *buffer, *replacement;
wordLen = 0;
// find beginning of word
for (column = spellConfig->Column, buffer = spellConfig->Buffer + column; column && IsASpace(*buffer, FALSE); --column)
--buffer;
while (column && !IsASpace(*(buffer - 1), FALSE)) {
--column;
--buffer;
}
// determine length
while (((column + wordLen) < spellConfig->Len) && !IsASpace(buffer[wordLen], FALSE))
++wordLen;
if (replacement = CheckThisWord(apiMsg, spellConfig, buffer, wordLen, ask, volume))
if (replacement != (UBYTE *)TRUE)
Replace(apiMsg, spellConfig, column, wordLen, replacement);
}
/* ------------------------------- FindNextError -------------------------------
Jump to next error
*/
LONG
FindNextError(apiMsg, spellConfig)
struct APIMessage *apiMsg;
struct SpellConfig *spellConfig;
{
struct EditConfig *config = apiMsg->api_Config;
UWORD wordLen, last;
char *buffer, *replacement;
while (DoNextWord(apiMsg, spellConfig, FALSE)) {
if (spellConfig->Len) {
static struct APIModifyRequest modifyRequest = { 0 };
// determine word length
buffer = spellConfig->Buffer + spellConfig->Column;
last = spellConfig->Len - spellConfig->Column;
wordLen = 0;
while ((wordLen < last) && !IsASpace(buffer[wordLen], FALSE))
++wordLen;
if (wordLen) {
// set editor's cursor position
modifyRequest.mr_Line = spellConfig->Line;
modifyRequest.mr_Column = spellConfig->Column;
apiMsg->api_Modify = &modifyRequest;
// check word, get replacement (if any)
if (replacement = CheckThisWord(apiMsg, spellConfig, buffer, wordLen, FALSE, spellConfig->Volume)) {
if (replacement != (char *)TRUE)
Replace(apiMsg, spellConfig, spellConfig->Column, wordLen, replacement);
return(RC_OK);
}
}
}
}
((struct APIRexxNotify *)apiMsg->api_Data)->arn_CommandError = spellConfig->AppStrings[NOMOREERRORS];
return(RC_WARN);
}
/* ------------------------------- CheckThisWord -------------------------------
Check word <check> of length <len>. Return NULL if ok, otherwise replacement
or TRUE. Ignore single characters.
*/
char *
CheckThisWord(apiMsg, spellConfig, check, len, ask, volume)
struct APIMessage *apiMsg;
struct SpellConfig *spellConfig;
char *check;
UWORD len, volume;
BOOL ask;
{
static char word[4096], result[4096];
UBYTE *replacement = NULL;
if ((len > 1) && ((spellConfig->TeX == FALSE) || (*check != 92))) {
movmem(check, word, len);
word[len] = 0;
strins(word, "QUICKCHECK ");
// make ISpell check word
if (SendRexxCommand("IRexxSpell", word, spellConfig->RexxPort, result)) {
if (stricmp(result, "ok")) {
replacement = (char *)TRUE;
Beep(volume);
// send additional CHECK command to get further information
if (ask && SendRexxCommand("IRexxSpell", word + 5, spellConfig->RexxPort, result)) {
if (*result == '&') {
// translate ISpell result string into exec list
struct List list;
struct Node *node, *nextNode;
UBYTE *start, *end, *selection;
// example ISpell result: "& hellu 2: hello hell"
if (end = strchr(result, ':')) {
NewList(&list);
while (*end) {
do {
*end++ = 0;
} while (*end && ((*end == ',') || (*end == ' ')));
start = end;
while (*end && (*end != 32) && (*end != ','))
++end;
if (node = (struct Node *)AllocVec(sizeof(struct Node), MEMF_ANY | MEMF_CLEAR)) {
// we point directly into the result string
node->ln_Name = start;
AddTail(&list, node);
}
}
if (selection = ShowSpell(apiMsg->api_Global->F_ScrnName, &list, word + 11))
replacement = strcpy(result, selection);
for (node = list.lh_Head; nextNode = node->ln_Succ; node = nextNode)
FreeVec(node);
}
}
}
}
}
}
return(replacement);
}
/* -------------------------------- AddCurrentWord -----------------------------
Add current word to dictionary
*/
LONG
AddCurrentWord(apiMsg, spellConfig)
struct APIMessage *apiMsg;
struct SpellConfig *spellConfig;
{
struct EditConfig *config = apiMsg->api_Config;
UWORD column, wordLen;
UBYTE *buffer;
column = spellConfig->Column;
buffer = spellConfig->Buffer + column;
wordLen = 0;
// find beginning of word
while (column && !IsASpace(*(buffer - 1), FALSE)) {
--column;
--buffer;
}
// determine length
while (((column + wordLen) < spellConfig->Len) && !IsASpace(buffer[wordLen], FALSE))
++wordLen;
if (wordLen) {
static char command[1000];
movmem(buffer, command, wordLen);
command[wordLen] = 0;
strins(command, "QUICKADD ");
SendRexxCommand("IRexxSpell", command, spellConfig->RexxPort, NULL);
++spellConfig->Count;
return(RC_OK);
}
else {
((struct APIRexxNotify *)apiMsg->api_Data)->arn_CommandError = spellConfig->AppStrings[NOWORDUNDERCURSOR];
return(RC_WARN);
}
}
///
/// "gui"
/* --------------------------------- ShowSpell ---------------------------------
Show ISpell suggestions. Return user selection or NULL.
*/
char *
ShowSpell(screen, list, title)
char *screen, *title;
struct List *list;
{
char *result = NULL;
struct Screen *scr;
if (scr = LockPubScreen(screen)) {
struct TextFont *font;
if (font = OpenDiskFont(scr->Font)) {
APTR visualInfo;
if (visualInfo = GetVisualInfoA(scr, NULL )) {
struct Gadget *context, *glist, *gad;
if (context = CreateContext(&glist)) {
UWORD ww, wh, offX, offY;
LONG displayWidth, displayHeight;
struct Window *win;
offX = scr->WBorLeft;
offY = scr->RastPort.TxHeight + scr->WBorTop + 1;
struct NewGadget newGad = {
offX,
offY,
ComputeX(font, 221),
ComputeY(font, 156),
NULL, scr->Font, 0, 0, visualInfo, 0
};
gad = CreateGadget(LISTVIEW_KIND, context, &newGad, GTLV_ShowSelected, NULL, GTLV_Labels, list, GTLV_Selected, 0, TAG_DONE);
rtGetVScreenSize(scr, &displayWidth, &displayHeight);
ww = ComputeX(font, 221);
wh = ComputeY(font, 156);
if (win = OpenWindowTags( NULL,
WA_Left, ((displayWidth - ww)>>1) - scr->ViewPort.DxOffset,
WA_Top, ((displayHeight - wh)>>1) - scr->ViewPort.DyOffset,
WA_Width, ww + offX + scr->WBorRight,
WA_Height, wh + offY + scr->WBorBottom,
WA_IDCMP, LISTVIEWIDCMP | IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY | IDCMP_RAWKEY,
WA_Flags, WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_SMART_REFRESH | WFLG_ACTIVATE,
WA_Gadgets, glist,
WA_Title, title,
WA_PubScreen, scr,
TAG_DONE
)) {
ULONG class;
UWORD active;
active = 0;
result = list->lh_Head->ln_Name;
struct IntuiMessage *msg;
struct Node *node;
GT_RefreshWindow(win, NULL);
do {
while (!(msg = GT_GetIMsg(win->UserPort)))
WaitPort(win->UserPort);
switch (class = msg->Class) {
case RAWKEY:
WORD step = (msg->Code == CURSORUP) ? -1 : 1;
if (node = SearchNode(list, active + step)) {
active += step;
result = node->ln_Name;
GT_SetGadgetAttrs(gad, win, NULL, GTLV_Selected, active, GTLV_MakeVisible, active, TAG_DONE);
}
break;
case IDCMP_VANILLAKEY:
if (msg->Code == 27)
class = CLOSEWINDOW;
if (msg->Code == 13)
class = IDCMP_GADGETUP;
break;
case IDCMP_GADGETUP:
result = SearchNode(list, active = msg->Code)->ln_Name;
break;
}
GT_ReplyIMsg(msg);
} while ((class & (GADGETUP | CLOSEWINDOW)) == NULL);
if (class == CLOSEWINDOW)
result = NULL;
CloseWindow(win);
}
}
FreeVisualInfo(visualInfo);
}
}
UnlockPubScreen(screen, scr);
}
return(result);
}
/* --------------------------------- ComputeX ----------------------------------
Resize element of width <value> according to current font
*/
UWORD
ComputeX(font, value)
UWORD value;
struct TextFont *font;
{
return((font->tf_XSize * value) / 8);
}
/* --------------------------------- ComputeY ----------------------------------
Resize element of height <value> according to current font
*/
UWORD
ComputeY(font, value)
UWORD value;
struct TextFont *font;
{
return((font->tf_YSize * value) / 8);
}
///
/// "ed"
/* ----------------------------------- Replace ---------------------------------
Replace word of length <wordLen> at position <pos> within current line by
string <replacement>
*/
void
Replace(apiMsg, spellConfig, column, wordLen, replacement)
struct APIMessage *apiMsg;
struct SpellConfig *spellConfig;
char *replacement;
UWORD column, wordLen;
{
static struct APIModifyRequest modifyRequest;
static UBYTE buffer[4096];
struct EditConfig *config = apiMsg->api_Config;
UWORD newLen;
UBYTE *next;
// copy current line
movmem(spellConfig->Buffer, buffer, spellConfig->Len);
newLen = strlen(replacement);
next = buffer + column;
// replace old word by selection from dictionary
if (newLen != wordLen)
movmem(next + wordLen, next + newLen, spellConfig->Len - column - wordLen);
movmem(replacement, next, newLen);
// make GoldED change line
modifyRequest.mr_Next = NULL;
modifyRequest.mr_Line = spellConfig->Line;
modifyRequest.mr_Column = spellConfig->Column;
modifyRequest.mr_Size = spellConfig->Len + (newLen - wordLen);
modifyRequest.mr_Data = buffer;
if (column <= spellConfig->Column)
modifyRequest.mr_Column += (newLen - wordLen);
apiMsg->api_Modify = &modifyRequest;
}
/* ---------------------------------- DoNextWord -------------------------------
Move cursor to beginning of next word. If <strict> is TRUE, only 32(dez) is
considered as 'space character', otherwise all non-alphanumerical characters
(e.g. ',') are considered as 'space', too. Return FALSE on failure (end of
text)
*/
BOOL
DoNextWord(apiMsg, spellConfig, strict)
struct APIMessage *apiMsg;
struct SpellConfig *spellConfig;
BOOL strict;
{
struct EditConfig *config = apiMsg->api_Config;
if (spellConfig->Line >= config->Lines)
return(FALSE);
else {
// look for end of current word
if ((spellConfig->Column + 1) >= spellConfig->Len) {
if ((spellConfig->Line + 1) >= config->Lines)
return(FALSE);
else
DoRightSmart(apiMsg, spellConfig);
}
else {
while (!IsASpace((spellConfig->Buffer)[spellConfig->Column], strict)) {
if (DoRightSmart(apiMsg, spellConfig)) {
if (spellConfig->Line >= config->Lines)
return(FALSE);
break;
}
}
}
// look for beginning of next word
if (spellConfig->Len) {
while (IsASpace((spellConfig->Buffer)[spellConfig->Column], strict)) {
if (DoRightSmart(apiMsg, spellConfig)) {
if (spellConfig->Line >= config->Lines)
return(FALSE);
else
break;
}
}
}
return(TRUE);
}
}
/* --------------------------------- IsASpace ----------------------------------
Return TRUE if character is considered to be a 'white space'. Only SPC is
considered to be white space if <strict> is TRUE.
*/
BOOL
IsASpace(code, strict)
UBYTE code;
BOOL strict;
{
if (strict)
return(code == 32);
else
return ((code < 'A') || ((code >= 128) && (code <= 160)) || strchr(".,;:!()[]{}-/»«", code));
}
/* ------------------------------- DoRightSmart --------------------------------
Move cursor right to non-space column if there are characters left, else jump to
beginning of next line. Return TRUE if passing end of line.
*/
BOOL
DoRightSmart(apiMsg, spellConfig)
struct APIMessage *apiMsg;
struct SpellConfig *spellConfig;
{
struct EditConfig *config = apiMsg->api_Config;
UBYTE *next, *last;
next = spellConfig->Buffer + spellConfig->Column;
last = spellConfig->Buffer + spellConfig->Len;
while (++next < last) {
if (*next != 32) {
DoRight(apiMsg, spellConfig);
return(FALSE);
}
}
if ((spellConfig->Line + 1) < config->Lines) {
spellConfig->Column = 0;
DoDown(apiMsg, spellConfig);
}
return(TRUE);
}
/* ---------------------------------- DoRight ----------------------------------
Move cursor one column right
*/
void
DoRight(apiMsg, spellConfig)
struct APIMessage *apiMsg;
struct SpellConfig *spellConfig;
{
if (spellConfig->Column != (EDIT_MAXWIDTH - 1))
++spellConfig->Column;
}
/* ---------------------------------- DoDown -------------------------------------
Move cursor down (but don't exeed last line of text).
*/
void
DoDown(apiMsg, spellConfig)
struct APIMessage *apiMsg;
struct SpellConfig *spellConfig;
{
struct EditConfig *config = apiMsg->api_Config;
if (++spellConfig->Line < config->Lines) {
struct LineNode *node = config->TextNodes + spellConfig->Line;
spellConfig->Buffer = node->Text;
spellConfig->Len = node->Len;
}
}
///
/// "misc"
/* ---------------------------------- Exists -----------------------------------
Check if file exists
*/
BOOL
Exists(file)
UBYTE *file;
{
BPTR lock;
if (lock = Lock(file, ACCESS_READ)) {
UnLock(lock);
return(TRUE);
}
else
return(FALSE);
}
/* ----------------------------------- Information -----------------------------
Show info requester
*/
UWORD
Information(apiMsg, body, button)
struct APIMessage *apiMsg;
char *body, *button;
{
return(rtEZRequestTags(body, button ? button : "_OK", NULL, NULL,
RT_Underscore, '_',
RTEZ_ReqTitle, "SpellIT",
RT_PubScrName, apiMsg ? apiMsg->api_Global->F_ScrnName : NULL,
TAG_DONE)
);
}
/* --------------------------------- AlertInfo -----------------------------------
Show alert. Returns TRUE if user pressed left button.
*/
BOOL
AlertInfo(text)
char *text;
{
BOOL result = FALSE;
if (text) {
char *buffer;
if (buffer = AllocVec(80, MEMF_PUBLIC | MEMF_CLEAR)) {
buffer[1] = '\30';
buffer[2] = '\25';
strcpy(buffer + 3, text);
result = DisplayAlert(RECOVERY_ALERT, buffer, 40);
FreeVec(buffer);
}
}
return(result);
}
/* ---------------------------------- EvaluateBool -----------------------------
Read slot <slot> of command array filled out by dos/ReadArgs(). Return code
depends on slot value: return TRUE if CommandArgs[<slot>] is pointing to string
"TRUE". Return !<oldValue> if it is pointing to "TOGGLE" (old value has to bee
valid). Return FALSE in any other case. This function does write to <oldValue>
(if valid).
*/
BOOL
EvaluateBool(oldValue, slot, argArray)
BOOL *oldValue;
ULONG *argArray;
UWORD slot;
{
BOOL result = oldValue ? *oldValue : FALSE;
if (argArray[slot]) {
result = stricmp((char *)argArray[slot], "TOGGLE") ? !stricmp((char *)argArray[slot], "TRUE") : !result;
if (oldValue)
*oldValue = result;
}
return(result);
}
/* ---------------------------------- SearchNode --------------------------------
Return pointer to node if ordinal number (0, ...) is known.
*/
struct Node *
SearchNode(list, ordinal)
struct List *list;
UWORD ordinal;
{
struct Node *node;
for (node = list->lh_Head; node->ln_Succ; --ordinal, node = node->ln_Succ)
if (!ordinal)
return(node);
return(NULL);
}
/* ----------------------------------- Beep ------------------------------------
Short audible beep
*/
void
Beep(volume)
UWORD volume;
{
if (volume) {
struct IOAudio *audioIO;
if (audioIO = (struct IOAudio *)AllocVec(sizeof(struct IOAudio), MEMF_PUBLIC | MEMF_CLEAR)) {
struct MsgPort *audioMP;
if (audioMP = CreateMsgPort()) {
UBYTE whichannel[] = { 1, 2, 4, 8 };
audioIO->ioa_Request.io_Message.mn_ReplyPort = audioMP;
audioIO->ioa_Request.io_Message.mn_Node.ln_Pri = 0;
audioIO->ioa_Request.io_Command = ADCMD_ALLOCATE;
audioIO->ioa_Request.io_Flags = ADIOF_NOWAIT;
audioIO->ioa_AllocKey = 0;
audioIO->ioa_Data = whichannel;
audioIO->ioa_Length = sizeof(whichannel);
if (!OpenDevice("audio.device", 0, (struct IORequest *)audioIO, 0)) {
__chip const static UBYTE waveptr[2] = {127, -127};
audioIO->ioa_Request.io_Message.mn_ReplyPort = audioMP;
audioIO->ioa_Request.io_Command = CMD_WRITE;
audioIO->ioa_Request.io_Flags = ADIOF_PERVOL;
audioIO->ioa_Data = waveptr;
audioIO->ioa_Length = 2;
audioIO->ioa_Period = 1015;
audioIO->ioa_Volume = volume;
audioIO->ioa_Cycles = 60; // 44;
BeginIO((struct IORequest *)audioIO );
WaitPort(audioMP);
GetMsg (audioMP);
CloseDevice((struct IORequest *)audioIO);
}
DeleteMsgPort(audioMP);
}
FreeVec(audioIO);
}
}
else
DisplayBeep(0);
}
///
/// "arexx"
/* ---------------------------------- SendRexxCommand -------------------------
Send ARexx message & wait for answer. Return pointer to result or NULL.
*/
ULONG *
SendRexxCommand(port, cmd, replyPort, buffer)
char *cmd, *port, *buffer;
struct MsgPort *replyPort;
{
struct MsgPort *rexxport;
Forbid();
if (rexxport = FindPort(port)) {
struct RexxMsg *rexxMsg, *answer;
if (rexxMsg = CreateRexxMsg(replyPort, NULL, NULL)) {
if (rexxMsg->rm_Args[0] = CreateArgstring(cmd, strlen(cmd))) {
static ULONG result;
rexxMsg->rm_Action = RXCOMM | RXFF_RESULT;
PutMsg(rexxport, &rexxMsg->rm_Node);
do {
WaitPort(replyPort);
if (answer = (struct RexxMsg *)GetMsg(replyPort))
result = answer->rm_Result1;
} while (!answer);
Permit();
if (answer->rm_Result1 == RC_OK) {
if (answer->rm_Result2) {
if (buffer)
strcpy(buffer, (char *)answer->rm_Result2);
DeleteArgstring((char *)answer->rm_Result2);
}
}
DeleteArgstring((char *)ARG0(answer));
DeleteRexxMsg(answer);
return(&result);
}
}
}
Permit();
return(NULL);
}
///